在JavaScript中,常常會看到複製/拷貝物件的問題,那複製物件又分成深拷貝跟淺拷貝。如果沒有分清當下的複製是哪一種拷貝,假如當有個B物件被A物件複製出來,並且修改B物件的內容,可能會同時更改到A物件的內容。
所謂在物件中的淺拷貝,會讓複製與被複製出來的物件屬性值指向一樣的物件來源。
JavaScript中,所有內建物件的複製方法都是淺拷貝,接下來使用Object.assign()
,這個方法是拿多個物件,並且複製到某個目標物件。
但如果是下面的寫法,實際上是沒有任何複製行為的:
const contestant = {
contestantId: 1,
contestantName: "Alice",
hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
}
const point = {
judgeRating: 9.5
}
const contestantData = Object.assign(contestant,point)
contestantData.contestantId = 30
console.log(contestant.contestantId);//30
console.log(contestantData.contestantId);//30
可以使用一個全新的物件去裝來源物件,發現修改跟新增屬性是不受影響的:
const contestant = {
contestantId: 1,
contestantName: "Alice",
hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
}
const contestantData = Object.assign({},contestant)
contestantData.contestantId = 30
console.log(contestant.contestantId);//1
console.log(contestantData.contestantId);//30
contestant.beveragePairing = "Jasmine tea";
console.log(contestantData.beveragePairing);//undefined
但要注意,修改hotpotIngredients
的內容會同時影響原物件跟被更改的物件:
const contestant = {
contestantId: 1,
contestantName: "Alice",
hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
}
const contestantData = Object.assign({},contestant)
contestant.hotpotIngredients[0] = null;
console.log(contestant.hotpotIngredients);// [ null, 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ]
console.log(contestantData.hotpotIngredients);// [ null, 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ]
坦白說,一些文章提到第一層
與第二層資料
有點難直覺理解與解釋,所以我會把外面裝進變數的物件或陣列當作第一層,在物件中只要有用到{}
或[]
的物件或陣列為基準來當第二層,以此類推物件中的任何更深層級的物件。
那麽淺拷貝的問題,像是在被複製的物件中第二層之後的資料不是複製過來的,只要有任何修改就會影響原物件。但以我需要的複製,是希望兩個物件之間的關係是徹底獨立的,這時候就需要使用深拷貝。
想使用深拷貝的話,有一些第三方套件可以幫忙,或者使用JSON.stringify()
轉換為JSON string,再透過JSON.parse()
解析JSON string的內容,轉換一個全新物件。
把剛剛的複製方法改成JSON,並且重新輸出一次陣列,會發現兩者之間互不影響。
const contestant = {
contestantId: 1,
contestantName: "Alice",
hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
}
const contestantData = JSON.parse(JSON.stringify(contestant));
contestant.hotpotIngredients[0] = null;
console.log(contestant.hotpotIngredients);//[ null, 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ]
console.log(contestantData.hotpotIngredients);//[ 'Beef slices', 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ]
Web API也有提供structuredClone() global function方法可以深拷貝,但如果要使用的話,就需要先注意程式的執行環境了。